package com.yc.utilslib.reader;

import android.os.Handler;
import android.os.Message;
import android.util.Log;

import com.yc.utilslib.reader.protocol.Command;
import com.yc.utilslib.reader.protocol.Defines;
import com.yc.utilslib.utils.ByteUtils;

import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.Queue;

/**
 * 作者：TanTun
 * 时间：2017/10/19
 * 邮箱：32965926@qq.com
 * 描述：
 */

public class Reader {

    private String mDevice;
    private int mBaud_rate = 115200;

    private Queue<byte[]> mFramesQueue = new LinkedList<>();
    private byte[] mBuffer = new byte[1024];
    private int mBufferIndex = 0;
    private int mFrameIndex = 0;

    private SerialPortFinder mSerialPortFinder = new SerialPortFinder();
    private SerialPort mSerialPort = null;
    private ReadThread mReadThread = null;
    private FrameHandleThread mFrameHandleThread = null;

    private Handler mHandlerCallBack = null;
    private Handler mHandlerError = null;

    public void setmHandlerError(Handler mHandlerError) {
        this.mHandlerError = mHandlerError;
    }

    public Reader(String device, int baud_rate) {
        mDevice = device;
        mBaud_rate = baud_rate;
    }

    public Boolean open(Handler callBack) {

        Boolean ret = false;

        try {
            String[] paths = mSerialPortFinder.getAllDevicesPath();

            for (String path : paths
                    ) {
                if (path.equals(mDevice)) {
                    /* Open the serial port */
                    mSerialPort = new SerialPort(new File(path), mBaud_rate, 0);

                    mReadThread = new ReadThread();
                    mReadThread.start();
                    mFrameHandleThread = new FrameHandleThread();
                    mFrameHandleThread.start();
                    break;
                }
            }

            if (mSerialPort != null) {
                mHandlerCallBack = callBack;
                ret = true;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return ret;
    }

    public void close() {
        if (mSerialPort != null) {
            mSerialPort.close();
            mSerialPort = null;
        }

        if (mFrameHandleThread != null && mFrameHandleThread.isAlive()) {
            mFrameHandleThread.interrupt();
            mFrameHandleThread = null;
        }

        if (mReadThread != null && mReadThread.isAlive()) {
            mReadThread.interrupt();
            mReadThread = null;
        }

        if (mHandlerCallBack != null) {
            mHandlerCallBack = null;
        }
    }

    public void restart() {

        if (mReadThread == null || !mReadThread.isAlive()) {
            if (mReadThread != null ) mReadThread.interrupt();
            mReadThread = null;
            mReadThread = new ReadThread();
            mReadThread.start();
        }

        if (mFrameHandleThread == null || !mFrameHandleThread.isAlive()) {
            if (mFrameHandleThread != null) mFrameHandleThread.interrupt();
            mFrameHandleThread = null;
            mFrameHandleThread = new FrameHandleThread();
            mFrameHandleThread.start();
        }
    }

    public void SetCommand(Command cmd) {
        if (mSerialPort != null && mSerialPort.getOutputStream() != null) {
            try {
                mSerialPort.getOutputStream().write(cmd.toArray());
                //mSerialPort.getOutputStream().write(ByteUtils.hexStringToByteArray("5AAA12080240010001084001006003400600FFFFFFFFFFFFCB6BBB"));
                //mSerialPort.getOutputStream().write(ByteUtils.hexStringToByteArray("5AAA3803251001000026100100000240010005094001000027100100010840010060034006001979D8704BDC0A400600597DE2704BDC011002000000B36BBB"));
                //mSerialPort.getOutputStream().write(ByteUtils.hexStringToByteArray("5AAA0004FB6BBB"));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private class ReadThread extends Thread {

        @Override
        public void run() {
            super.run();
            while (!isInterrupted() && mSerialPort != null) {
                int size;
                try {
                    byte[] buffer = new byte[128];
                    if (mSerialPort.getInputStream() == null) return;
                    size = mSerialPort.getInputStream().read(buffer);
                    Log.v("reader", "接收到[" + size + "]字节的数据！");
                    if (size > 0) {
                        //逐字节解析数据
                        for (int i = 0; i < size; i ++) {
                            if (mBufferIndex >= 1024 || mFrameIndex < 0) {
                                mBufferIndex = 0;
                                mFrameIndex = 0;
                            }

                            if (mFrameIndex <= 0 && buffer[i] == Defines.PROTOCOL_HEADING1) {
                                mBufferIndex  = 0;
                            } else if (mFrameIndex == 0 && mBufferIndex == 2 &&
                                    mBuffer[0] == Defines.PROTOCOL_HEADING1 &&
                                    mBuffer[1] == Defines.PROTOCOL_HEADING2) {
                                mFrameIndex = (buffer[i] & 0x0FF) + 5;
                            }

                            mBuffer[mBufferIndex] = buffer[i];
                            mBufferIndex = mBufferIndex + 1;

                            if (mFrameIndex > 0) mFrameIndex = mFrameIndex - 1;

                            //当前帧中对应长度的数据已经接收完成，验证数据是否正确
                            if (mBufferIndex > 3 && mFrameIndex == 0) {
                                //首先验证帧尾是否正确
                                if (mBuffer[mBufferIndex - 2] == Defines.PROTOCOL_ENDING1
                                        && mBuffer[mBufferIndex - 1] == Defines.PROTOCOL_ENDING2) {
                                    /*-----------------进行CRC校验计算------------------*/
                                    int len = mBuffer[2] & 0x0FF;
                                    if (len + 7 == mBufferIndex
                                            && ByteUtils.byteToUnsignedByte(mBuffer[mBufferIndex - 3]) ==
                                            ByteUtils.calculationCRC(mBuffer, 2, len + 2)) {
                                        byte[] frame = new byte[mBufferIndex];
                                        System.arraycopy(mBuffer, 0, frame, 0, mBufferIndex);
                                        mFramesQueue.offer(frame);
                                    }
                                    /*-----------------------------------------------*/
                                }
                                mBufferIndex = 0;
                            }
                        }
                    }

                    if (!mFrameHandleThread.isAlive()) {
                        if (mHandlerError != null) {
                            mHandlerError.sendMessage(new Message());
                        }
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                    return;
                }
            }
        }
    }

    private class FrameHandleThread extends Thread {

        @Override
        public void run() {
            super.run();
            while (!isInterrupted() && mSerialPort != null) {
                try {
                    while (!mFramesQueue.isEmpty()) {
                        byte[] frame = mFramesQueue.poll();

                        if (mHandlerCallBack != null) {
                            Message msg = new Message();
                            msg.arg1 = 0;
                            msg.obj = Command.parsing(frame);
                            mHandlerCallBack.sendMessage(msg);
                        }
                    }

                    for (int i = 0; i < 30 * 100; i++) //每分钟调度一次
                    {
                        if (isInterrupted() || !mFramesQueue.isEmpty() || mSerialPort == null)
                            break;
                        Thread.sleep(10);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    return;
                }
            }
        }
    }
}
